#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <time.h>
#define ERROR_CHECK(ret, num, msg) \
    {                              \
        if (ret == num)            \
        {                          \
            perror(msg);           \
            exit(1);               \
        }                          \
    }
#define ARGS_CHECK(argc, num)                 \
    {                                         \
        if (argc != num)                      \
        {                                     \
            fprintf(stderr, "args error!\n"); \
            return -1;                        \
        }                                     \
    }
char getFileType(__mode_t mode)
{
    if (S_ISDIR(mode))
        return 'd';
    if (S_ISBLK(mode))
        return 'b';
    if (S_ISCHR(mode))
        return 'c';
    if (S_ISFIFO(mode))
        return 'p';
    if (S_ISLNK(mode))
        return 'l';
    if (S_ISSOCK(mode))
        return 's';
    if (S_ISREG(mode))
        return '-';
    return '?';
}

char *get_permissions(__mode_t mode)
{
    static char permissions[10];
    strcpy(permissions, "rwxrwxrwx");
    permissions[9] = '\0';
    for (size_t i = 0; i < 9; i++)
    {
        if (!(mode & 1 << (8 - i))) // mode &100  ; mode & 010 ; mode & 001;
        {
            permissions[i] = '-';
        }
    }

    return permissions;
}
void show(char **p, int index)
{
    for (size_t i = 0; i < index; i++)
    {
        if (p[i][0] == 'd')
        {
            printf("\033[34m%s\033[0m\n", p[i]);
        }
        else if (p[i][3] == 'x')
        {
            printf("\033[32m%s\033[0m\n", p[i]);
        }
        else
        {
            printf("%s", p[i]);
        }
    }
}
void insertSort(struct dirent **pathMsg, int index)
{
    int j;
    struct dirent *key;
    for (size_t i = 1; i < index; i++)
    {
        key = pathMsg[i];
        j = i - 1;
        while (j >= 0 && strcmp(pathMsg[j]->d_name, key->d_name) > 0)
        {
            pathMsg[j + 1] = pathMsg[j];
            j--;
        }
        pathMsg[j + 1] = key;
    }
}
int main(int argc, char *argv[])
{
    ARGS_CHECK(argc, 2);
    DIR *dirp = opendir(argv[1]);
    ERROR_CHECK(NULL, dirp, "opendir");
    int ret = chdir(argv[1]);
    ERROR_CHECK(ret, -1, "chdir");
    struct dirent *dirpStructure;
    struct stat statBuf;
    int size = 32;
    int index = 0;

    struct dirent **pathMsg = (struct dirent **)malloc(size * sizeof(struct dirent));
    while (dirpStructure = readdir(dirp))
    {
        if (strcmp(dirpStructure->d_name, ".") == 0 || strcmp(dirpStructure->d_name, "..") == 0)
            continue;

        if (index == size)
        {
            size = size * 2;
            pathMsg = (struct dirent **)realloc(pathMsg, size * sizeof(struct dirent));
        }
        pathMsg[index++] = dirpStructure;
    }
    insertSort(pathMsg, index);

    for (size_t i = 0; i < index; i++)
    {
        int ret = stat(pathMsg[i]->d_name, &statBuf);

        ERROR_CHECK(-1, ret, "stat");
        char file_type = getFileType(statBuf.st_mode);
        char permission[20] = {0};
        strcpy(permission, get_permissions(statBuf.st_mode));
        struct passwd *uid = getpwuid(statBuf.st_uid);
        struct group *gid = getgrgid(statBuf.st_gid);
        char mtime[20];
        strftime(mtime, sizeof(mtime), "%b %d %H:%M", localtime(&statBuf.st_mtime));

        if (file_type == 'd')
        {

            printf("%c%s %2ld %s %s %6ld %s \033[34m%s\033[0m\n", file_type,
                   permission,
                   statBuf.st_nlink,
                   uid->pw_name,
                   gid->gr_name,
                   statBuf.st_size,
                   mtime,
                   pathMsg[i]->d_name);
        }
        else if (permission[2] == 'x' || permission[5] == 'x' || permission[8] == 'x')
        {
            printf("%c%s %2ld %s %s %6ld %s \033[32m%s\033[0m\n", file_type,
                   permission,
                   statBuf.st_nlink,
                   uid->pw_name,
                   gid->gr_name,
                   statBuf.st_size,
                   mtime,
                   pathMsg[i]->d_name);
        }
        else
        {

            printf("%c%s %2ld %s %s %6ld %s %s\n", file_type,
                   permission,
                   statBuf.st_nlink,
                   uid->pw_name,
                   gid->gr_name,
                   statBuf.st_size,
                   mtime,
                   pathMsg[i]->d_name);
        }
    }
    free(pathMsg);

    return 0;
}